using Slaks.DocumentSerialization.Document;
using Slaks.DocumentSerialization.Document.Wordnet;
using Slaks.Graphs.Comparators;
using Slaks.Graphs.Wordnet;
using DocumentWordnet = Slaks.DocumentSerialization.Document.Wordnet;
using GraphWordnet = Slaks.Graphs.Wordnet;
using Synonym = Slaks.DocumentSerialization.Document.Wordnet.Synonym;

namespace Slaks.Graphs.GraphBuilder.Wordnet
{
	public class WordnetSynGraphBuilder
	{
		/// <summary>
		/// Removes the rest of infrequent vertices leaving at most (numberOfVerticesToLeave)
		/// Used for temporal graph for quering about existing vertices
		/// </summary>
		/// <param name="numberOfVerticesToLeave"></param>
		public void RemoveInfrequentVerticesInTemporalGraph(WordnetGraph temporalGraph, int numberOfVerticesToLeave)
		{
			VertexList vertexList = temporalGraph.GetVertexList();
			Id2VertexMap id2VertexMapper = temporalGraph.GetId2VertexMap();
			if (numberOfVerticesToLeave <= 0) return;
			int totalCount = vertexList.Count;
			if (numberOfVerticesToLeave >= totalCount) return;
			//what is desired length of the vertex list

			temporalGraph.SortVertices(new FrequencyComparer());

			int currentIndex = numberOfVerticesToLeave;

			long idToRemove;
			while (vertexList.Count > numberOfVerticesToLeave)
			{
				idToRemove = vertexList[currentIndex].GetId();
				vertexList.RemoveAt(currentIndex);
				id2VertexMapper.Remove(idToRemove);
			}

		}

		/// <summary>
		/// Builds the temporal graph only with vertices
		/// </summary>
		/// <param name="document"></param>
		/// <returns></returns>
		protected WordnetGraph BuildTemporalGraph(WordnetDocument document)
		{
			WordnetVertex vertex;
			WordnetGraph tempGraph = new WordnetGraph();

			//create temporal graph
			//iterate through nodes
			//double senseProbability;
			foreach (WordnetNode node in document.Nodes)
			{
				vertex = tempGraph.CreateAddVertex(node.Id);
				//senseProbability = this.GetSenseProbability(node);
				vertex.Frequency = node.TotalTermFrequency; //*senseProbability;
			}
			return tempGraph;
		}

		protected void CopySynonyms(WordnetNode node, WordnetVertex vertex)
		{
			foreach (Synonym synonyms in node.SynonymList)
			{
                GraphWordnet.Synonym syn = new GraphWordnet.Synonym(node.Id);
				foreach (long id in synonyms.SynonymIdList())
				{
					syn.GetSynonymList().Add(id);
				}
				vertex.GetSynonymBundle().Add(syn);
			}
		}

		protected double GetSenseProbability(WordnetNode node)
		{
			//if (node.SenseInfo.GetTotalSenses() <= 2) return 0.1;
			//else if (node.SenseInfo.GetTotalSenses() >= 10) return 0.001;
			//return ((double) 1/node.SenseInfo.GetTotalSenses());
			double noun = node.SenseInfo.Nouns;
			double verb = node.SenseInfo.Verbs;
			double adj = node.SenseInfo.Adjectives;
			double adv = node.SenseInfo.Adverbs;

			if (noun > verb)
			{
				if (noun > adj && noun > adv) return 1;
				else return 0.1;
			}
			else
			{
				if (verb > adj && verb > adv) return 0.2;
				else return 0.1;
			}
		}

		/// <summary>
		/// Add edges between nodes
		/// </summary>
		/// <param name="document"></param>
		/// <param name="graph">graph to build</param>
		/// <param name="temporalGraph">temporal graph which hold only frequent nodes. it will be queried for them</param>
		/// <param name="currentVertex">the current node</param>
		/// <param name="nParameter">the n-distance parameter</param>
		/// <param name="nDistanceCount">current distance counter</param>
		/// <param name="sentence">current sentence</param>
		/// <param name="sectionType">section type</param>
		protected void AddRelations(WordnetDocument document,
		                            WordnetGraph graph,
		                            WordnetGraph temporalGraph,
		                            WordnetVertex currentVertex,
		                            int nParameter,
		                            int nDistanceCount,
		                            Sentence sentence,
		                            Section.SectionType sectionType,
		                            BuilderType builderType
			)
		{
			if (nDistanceCount > nParameter) return;
			WordnetNode node;
			long nextId;
			Vertex nextIdVertex;
			if (sentence.NextId >= 0)
			{
				//get the nextId.
				nextId = sentence.NextId;
				//we must check whether the nextId is in the list of frequent nodes.
				//if not then we must skip it.

				nextIdVertex = temporalGraph.GetVertex(nextId);

				node = document.GetNodeById(nextId);
				Section section = node.GetSectionByType(sectionType);
				if (section == null) return;
				Sentence nextSentence = section.GetSentence(sentence.Number, sentence.Place + 1);
				if (nextSentence == null) return;

				string sectionLabel;

				if (nextIdVertex != null) //the nextIdVertex is among frequent nodes
				{
					if (builderType == BuilderType.WordnetSynStandardRepresentation) sectionLabel = section.GetSectionTypeString();
						//else if (builderType == BuilderType.WordnetRawFrequencyRepresentation) sectionLabel = "raw";
						//else if (builderType == BuilderType.WordnetNormalizedFrequencyRepresentation) sectionLabel = "norm";
					else throw new GraphException("AddRelations() : WordnetGraph representation not supported");

					nextIdVertex = graph.CreateAddVertex(nextId);
					currentVertex.AddEdge(nextIdVertex, sectionLabel);

					AddRelations(document, graph, temporalGraph, currentVertex, nParameter, nDistanceCount + 1, nextSentence, sectionType, builderType);
				}
				else
				{
					//the nextIdVertex is not frequent, then we must skip it and find the frequent node.
					AddRelations(document, graph, temporalGraph, currentVertex, nParameter, nDistanceCount, nextSentence, sectionType, builderType);
				}
			}
			return;
		}

		/// <summary>
		/// Add edges between nodes
		/// </summary>
		/// <param name="document"></param>
		/// <param name="graph">graph to build</param>
		/// <param name="temporalGraph">temporal graph which hold only frequent nodes. it will be queried for them</param>
		/// <param name="currentVertex">the current node</param>
		/// <param name="nParameter">the n-distance parameter</param>
		/// <param name="nDistanceCount">current distance counter</param>
		/// <param name="sentence">current sentence</param>
		/// <param name="sectionType">section type</param>
		protected void AddRelationsBiderectional(WordnetDocument document,
		                                         WordnetGraph graph,
		                                         WordnetGraph temporalGraph,
		                                         WordnetVertex currentVertex,
		                                         int nParameter,
		                                         int nDistanceCount,
		                                         Sentence sentence,
		                                         Section.SectionType sectionType,
		                                         BuilderType builderType
			)
		{
			if (nDistanceCount > nParameter) return;
			WordnetNode node;
			long nextId;
			WordnetVertex nextIdVertex;
			if (sentence.NextId >= 0)
			{
				//get the nextId.
				nextId = sentence.NextId;
				//we must check whether the nextId is in the list of frequent nodes.
				//if not then we must skip it.

				nextIdVertex = temporalGraph.GetVertex(nextId);

				node = document.GetNodeById(nextId);
				Section section = node.GetSectionByType(sectionType);
				if (section == null) return;
				Sentence nextSentence = section.GetSentence(sentence.Number, sentence.Place + 1);
				if (nextSentence == null) return;

				string sectionLabel;

				if (nextIdVertex != null) //the nextIdVertex is among frequent nodes
				{
					if (builderType == BuilderType.WordnetSynStandardRepresentation) sectionLabel = section.GetSectionTypeString();
						//else if (builderType == BuilderType.WordnetRawFrequencyRepresentation) sectionLabel = "raw";
						//else if (builderType == BuilderType.WordnetNormalizedFrequencyRepresentation) sectionLabel = "norm";
					else throw new GraphException("AddRelationsBidirectional() : Graph representation not supported");

					nextIdVertex = graph.CreateAddVertex(nextId);
					currentVertex.AddEdge(nextIdVertex, sectionLabel);
					nextIdVertex.AddEdge(currentVertex, sectionLabel);

					AddRelationsBiderectional(document, graph, temporalGraph, currentVertex, nParameter, nDistanceCount + 1, nextSentence, sectionType, builderType);
				}
				else
				{
					//the nextIdVertex is not frequent, then we must skip it and find the frequent node.
					AddRelationsBiderectional(document, graph, temporalGraph, currentVertex, nParameter, nDistanceCount, nextSentence, sectionType, builderType);
				}
			}
			return;
		}

	}
}